﻿using System;
using System.Diagnostics;
using System.Threading;
using Jiubang.Collections;
using log4net;

namespace Jiubang.Threads {
    /// <summary>
    ///   线程池管理类
    /// </summary>
    public sealed class TimeLimitedTaskManager : IDisposable {
        private static readonly ILog _log;
        private readonly int _maxConcurrency;
        private readonly int _maxQueueLength;
        private readonly ConcurrentLinkedQueue<ITimeLimitedTask> _queue;
        private readonly LockFreePool<ActionChangableTimer<Thread>> _timerPool;

        private int _currentConcurrency;
        private int _currentQueueLength;
        private bool _isDisposed;

        static TimeLimitedTaskManager() {
            _log = LogManager.GetLogger(typeof(TimeLimitedTaskManager));
        }

        /// <summary>
        ///   构造线程池对象
        /// </summary>
        /// <param name = "maxConcurrency">线程数</param>
        /// <param name = "maxQueueLength">最大队列长度。超过长度上限Add方法返回False</param>
        public TimeLimitedTaskManager(int maxConcurrency, int maxQueueLength) {
            if (maxConcurrency < 1)
                throw new ArgumentOutOfRangeException("maxConcurrency");
            if (maxQueueLength < 10)
                throw new ArgumentOutOfRangeException("maxQueueLength");

            _maxConcurrency = maxConcurrency;
            _maxQueueLength = maxQueueLength;

            _queue = new ConcurrentLinkedQueue<ITimeLimitedTask>();
            _timerPool = new LockFreePool<ActionChangableTimer<Thread>>(maxConcurrency, null, () => new ActionChangableTimer<Thread>());

            if (_log.IsInfoEnabled) {
                _log.Info(new StackTrace().ToString());
            }
        }

        #region IDisposable Members

        /// <summary>
        ///   释放未应用线程。并添加结束标识。
        /// </summary>
        public void Dispose() {
            _isDisposed = true;
            _timerPool.Dispose();
        }

        #endregion

        /// <summary>
        ///   把需要运行的对象加入到队列。队列已经满返回false
        /// </summary>
        /// <param name = "timeLimitedTask"></param>
        public bool Queue(ITimeLimitedTask timeLimitedTask) {
            if (timeLimitedTask == null)
                throw new ArgumentNullException();

            if (Interlocked.Increment(ref _currentQueueLength) > _maxQueueLength) {
                //等待队列已满时不再加入队列
                Interlocked.Decrement(ref _currentQueueLength);
                return false;
            }

            //简化流程所有请求先放入队列
            _queue.Enqueue(timeLimitedTask);

            if (Interlocked.Increment(ref _currentConcurrency) > _maxConcurrency) {
                //并发量已达上限时放入等待队列
                Interlocked.Decrement(ref _currentConcurrency);
            } else if (!_isDisposed) {
                ThreadPool.QueueUserWorkItem(Process);
            }
            return true;
        }

        private void Process(object state) {
            using (var timer = _timerPool.Take()) {
                try {
                    ITimeLimitedTask task;
                    while (!_isDisposed && _queue.TryDequeue(out task)) {
                        Interlocked.Decrement(ref _currentQueueLength);

                        var timeoutMilliSeconds = (int)task.ExecutionTimeout.TotalMilliseconds;
                        timer.Item.Change(AbortThread, Thread.CurrentThread, timeoutMilliSeconds, Timeout.Infinite);

                        try {
                            task.Execute();
                            timer.Item.Reset();
                        } catch (ThreadAbortException threadAbortException) {
                            Thread.ResetAbort();
                            _log.Warn(task.Name + "任务过预设上限" + timeoutMilliSeconds + "ms", threadAbortException);
                        } catch (Exception e) {
                            _log.Warn(task.Name + "任务执行时出现异常", e);
                        }
                    }
                } catch (Exception e) {
                    _log.Error(e);
                } finally {
                    Interlocked.Decrement(ref _currentConcurrency);
                }
            }
        }

        private static void AbortThread(Thread thread) {
            thread.Abort();
        }

        /// <summary>
        /// 支持定义执行超时的命令类
        /// </summary>
        public interface ITimeLimitedTask {
            /// <summary>
            /// 命令的名称。日志中显示该字符串标识。
            /// </summary>
            string Name { get; }

            /// <summary>
            /// 获取指定当前命令的超时时间
            /// </summary>
            TimeSpan ExecutionTimeout { get; }

            /// <summary>
            /// 待执行的命令。
            /// 当前方法不需要作异常处理。由线程池处理捕获异常。
            /// 如需要对数据完整性处理应catch ThreadAbortException
            /// </summary>
            void Execute();
        }
    }
}